#include "ObjectVisitor.h"

#include <tinyxml2.h>

#include "Memory.h"
#include "Logging.h"

le::ObjectVisitor::ObjectVisitor()
: m_IsReading(false)
, m_XmlDoc(LeNew tinyxml2::XMLDocument())
, m_CurrentXmlElement(NULL)
, m_SibblingElement(NULL)
{

}

le::ObjectVisitor::~ObjectVisitor()
{
	m_CurrentXmlElement = NULL;
	m_SibblingElement = NULL;
	LeDelete(m_XmlDoc);
}

bool le::ObjectVisitor::Load( const char* fileName )
{
	Reset();

	tinyxml2::XMLError error = m_XmlDoc->LoadFile(fileName);
	if (error != tinyxml2::XML_NO_ERROR)
	{
		LOG(eLog_Error, "failed to load xml: %s - Error was: %i", fileName, error);
		return false;
	}

	m_CurrentXmlElement = m_XmlDoc->FirstChildElement("root");
	if (m_CurrentXmlElement == NULL)
	{
		LOG(eLog_Error, "failed to find the root element in: %s", fileName);
	}

	m_IsReading = true;
	return true;
}

bool le::ObjectVisitor::Load( const le::Path& fileName )
{
	return Load(fileName.ToChar());
}

bool le::ObjectVisitor::Save( const char* fileName )
{
	tinyxml2::XMLError error = m_XmlDoc->SaveFile(fileName);
	if (error != tinyxml2::XMLError::XML_NO_ERROR)
	{
		LOG(eLog_Error, "failed to save xml: %s - Error was: %i", fileName, error);
		return false;
	}
	Reset();
	return true;
}

void le::ObjectVisitor::MarkForWriting()
{
	m_XmlDoc->Clear();
	m_CurrentXmlElement = m_XmlDoc->NewElement("root");
	m_XmlDoc->InsertFirstChild(m_CurrentXmlElement);
	m_IsReading = false;
}

bool le::ObjectVisitor::EnterNode( const char* nodeName )
{
	if (m_IsReading)
	{
		tinyxml2::XMLElement* element = m_CurrentXmlElement->FirstChildElement(nodeName);
		if (element)
		{
			m_CurrentXmlElement = element;
			return true;
		}
		return false;
	}
	else
	{
		tinyxml2::XMLElement* element = m_XmlDoc->NewElement(nodeName);
		m_CurrentXmlElement->InsertFirstChild(element);
		m_CurrentXmlElement = element;
	}
	
	return true;
}

bool le::ObjectVisitor::ExitNode()
{
	tinyxml2::XMLNode* parentNode = m_CurrentXmlElement->Parent();
	if (parentNode)
	{
		tinyxml2::XMLElement* parentElement = parentNode->ToElement();
		if (parentElement)
		{
			m_CurrentXmlElement = parentElement;
			return true;
		}
	}
	return false;
}

int32 le::ObjectVisitor::GetCount( const char* nodeName ) const
{
	int32 count = 0;
	tinyxml2::XMLElement* element = m_CurrentXmlElement->FirstChildElement(nodeName);
	if (element)
	{
		do 
		{
			++count;
			element->NextSiblingElement(nodeName);
		} 
		while (element);
	}
	return count;
}

bool le::ObjectVisitor::Visit( const char* name, int32& value, const int32 defaultValue)
{
	if (m_IsReading)
	{
		tinyxml2::XMLElement* nextElement = m_SibblingElement ? m_SibblingElement->NextSiblingElement(name) :  m_CurrentXmlElement->FirstChildElement(name);
		if (!nextElement)
		{
			return false;
		}
		m_SibblingElement = nextElement;

		tinyxml2::XMLError error = tinyxml2::XMLError::XML_NO_ERROR;
		int tempVal = value;
		error = m_SibblingElement->QueryIntAttribute("value", &tempVal);

		if (error != tinyxml2::XMLError::XML_NO_ERROR)
		{
			value = defaultValue;
		}
		
	}
	else
	{
		tinyxml2::XMLElement* element = m_XmlDoc->NewElement(name);
		element->SetAttribute("value", static_cast<int>(value));
		element->SetAttribute("type", eObjectType_Int32);
		m_CurrentXmlElement->InsertFirstChild(element);
	}

	return true;
}

bool le::ObjectVisitor::Visit( const char* name, float& value, const float defaultValue )
{
	if (m_IsReading)
	{
		tinyxml2::XMLElement* nextElement = m_SibblingElement ? m_SibblingElement->NextSiblingElement(name) :  m_CurrentXmlElement->FirstChildElement(name);
		if (!nextElement)
		{
			return false;
		}
		m_SibblingElement = nextElement;

		tinyxml2::XMLError error = tinyxml2::XMLError::XML_NO_ERROR;
		float tempVal = value;
		error = m_SibblingElement->QueryFloatAttribute("value", &tempVal);

		if (error != tinyxml2::XMLError::XML_NO_ERROR)
		{
			value = defaultValue;
		}
	}
	else
	{
		tinyxml2::XMLElement* element = m_XmlDoc->NewElement(name);
		element->SetAttribute("value", value);
		element->SetAttribute("type", eObjectType_Float);
		m_CurrentXmlElement->InsertFirstChild(element);
	}
	return true;
}

bool le::ObjectVisitor::Visit( const char* name, std::string& value, const std::string& defaultValue )
{
	if (m_IsReading)
	{
		tinyxml2::XMLElement* nextElement = m_SibblingElement ? m_SibblingElement->NextSiblingElement(name) :  m_CurrentXmlElement->FirstChildElement(name);
		if (!nextElement)
		{
			return false;
		}
		m_SibblingElement = nextElement;

		const char* retValue = m_SibblingElement->Attribute("value");

		if (retValue == NULL)
		{
			value = defaultValue;
		}
		else
		{
			value = retValue;
		}
	}
	else
	{
		tinyxml2::XMLElement* element = m_XmlDoc->NewElement(name);
		element->SetAttribute("value", value.c_str());
		element->SetAttribute("type", eObjectType_String);
		m_CurrentXmlElement->InsertFirstChild(element);
	}
	return true;
}

void le::ObjectVisitor::Reset()
{
	m_CurrentXmlElement = NULL;
	m_SibblingElement = NULL;
	m_XmlDoc->Clear();
}
